/**
 * Copyright (c) 2020 kedacom
 * OpenATC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 * http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 **/
package com.openatc.agent.realm;

import com.openatc.agent.model.User;
import com.openatc.agent.service.PermissionDao;
import com.openatc.agent.service.UserDao;
import com.openatc.agent.utils.TokenUtil;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;

import static com.openatc.core.common.IErrorEnumImplOuter.E_3024;
import static com.openatc.core.common.IErrorEnumImplOuter.E_3025;

public class MyRealm extends AuthorizingRealm {

    @Autowired(required = false)
    @Lazy
    private UserDao userDao;
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired(required = false)
    @Lazy
    private PermissionDao permissionDao;

    @Autowired
    @Lazy
    private TokenUtil tokenUtil;

    private Logger logger = LoggerFactory.getLogger(MyRealm.class);

    @Override
    public boolean supports(AuthenticationToken token) {
        //表示此Realm只支持JwtToken类型
        return token instanceof JwtToken;
    }

    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        User user = (User) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = createAuthorizationInfo(user.getUser_name());
//        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//        List<Permission> permissions = permissionDao.getPermissionsByUserName(user.getUser_name());
//        for (Permission permission : permissions) {
//            info.addStringPermission(permission.getPermission_code());
//        }
//        List<String> roles = userDao.getRoleNamesByUsername(user.getUser_name());
//        if (!CollectionUtils.isEmpty(roles)){
//            info.addRoles(roles);
//        }
        return info;
    }

    private SimpleAuthorizationInfo createAuthorizationInfo(String user_name) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        String sql = String.format("SELECT p.permission_code,r.name FROM t_user u " +
                " JOIN t_user_role ur ON u.id = ur.user_id " +
                " JOIN t_role r ON r.id = ur.role_id " +
                " JOIN t_role_permission rp ON rp.role_id = ur.role_id " +
                " JOIN t_permission p ON p.id = rp.permission_id WHERE user_name = '%s'",user_name);
        List<Map<String, Object>> infos = jdbcTemplate.queryForList(sql);

        if (!CollectionUtils.isEmpty(infos)) {
            for (Map<String,Object> infoMap : infos) {
                String permission = (String) infoMap.get("permission_code");
                String roleName = (String) infoMap.get("name");
                info.addRole(roleName);
                info.addStringPermission(permission);
            }
        }

        return info;
    }

    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        JwtToken jwtToken = (JwtToken) authenticationToken;
        String token = jwtToken.getToken();
        String ip = jwtToken.getIp();
        if (token == null) {
            throw new AuthenticationException("Token is null!");
        }
        // 验证token是否启用以及是否在有效期
        TokenUtil.checkToken(token);
//        // 1、验证token是否在有效期
//        boolean inValidDate = tokenUtil.isTokenInValidDate(token);
//        if (!inValidDate) {
//            throw new AuthenticationException("Token is expired!");
//        }
//        // 2、验证token是否启用
//        boolean isUsable = tokenUtil.isTokenUsable(token);
//        if (!isUsable) {
//            throw new AuthenticationException(E_3025.getErrorMsg());
//        }
        // 3、验证用户名
        String username = tokenUtil.getUsernameFromToken(token);

        if (username == null) {
            throw new AuthenticationException("Get null username from token!");
        }
        User user = userDao.getUserByUserName(username);
        // 如果上面的验证通过，但数据库中没有保存相关用户，认为是全息创建的token，应答错误码，提示创建该用户
        if (user == null) {
            throw new AuthenticationException(E_3024.getErrorMsg());
        }

        // 4、判断用户是否停用
        if (user.getStatus() == 0) {
            throw new AuthenticationException("Account has been deactivated!");
        }
//        if (!tokenUtil.checkip(ip, token)) {
//            throw new AuthenticationException("access ip is inconsistent with user ip!");
//        }

        try {
            return new SimpleAuthenticationInfo(user, token, getName());
        } catch (Exception e) {
            throw new AuthenticationException(e);
        }
    }
}

